package com.basker.pisces.core.meta.bean;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import com.basker.pisces.constant.Constants;
import com.basker.pisces.core.meta.IDataObjectMeta;
import com.basker.pisces.core.meta.annotation.DataObjectMeta;
import com.basker.pisces.spring.SpringApplicationContext;

/**
 * 扫描所有有{@link DataObjectMeta}注解的类，封装成{@link BeanMetaTypeWrapper}注册到IOC中
 *
 * @author hangwen
 */
public class BeanMetaTypeWrapperRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {
    private Environment environment;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false,
                environment);

        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(DataObjectMeta.class);
        scanner.addIncludeFilter(annotationTypeFilter);

        Set<BeanDefinition> findCandidateComponents = new LinkedHashSet<>();
        findCandidateComponents.addAll(scanner.findCandidateComponents(Constants.BASE_PACKAGE_NAME));

        String basePackage = ClassUtils.getPackageName(importingClassMetadata.getClassName());
        if (!basePackage.equals(Constants.BASE_PACKAGE_NAME)) {
            findCandidateComponents.addAll(scanner.findCandidateComponents(basePackage));
        }

        for (BeanDefinition candidateComponent : findCandidateComponents) {
            if (candidateComponent instanceof AnnotatedBeanDefinition) {
                AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();

                registerBeanMeta(registry, candidateComponent, annotationMetadata);
            }

        }
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    private void registerBeanMeta(BeanDefinitionRegistry registry, BeanDefinition candidateComponent,
                                  AnnotationMetadata annotationMetadata) {
        Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(DataObjectMeta.class.getName());
        Object tableName = attributes != null ? attributes.get("tableName") : null;
        if (StringUtils.isEmpty(tableName)) {
            // 只注册有数据表的pojoMeta
            return;
        }

        BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(BeanMetaTypeWrapper.class);

        definition.addPropertyValue("beanType", candidateComponent.getBeanClassName());
        AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
        beanDefinition.setDependsOn(StringUtils.uncapitalize(SpringApplicationContext.class.getSimpleName()));

        String className = annotationMetadata.getClassName();

        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className + "$TypeWrapper", null);
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);

    }

    public static class BeanMetaTypeWrapper {

        private Class<?> beanType;

        public static List<BeanMetaTypeWrapper> findBySchema(List<BeanMetaTypeWrapper> beanMetaTypeWrappers,
                                                             String schema, String defaltSchema) {
            List<BeanMetaTypeWrapper> ret = new ArrayList<>(beanMetaTypeWrappers.size());

            for (BeanMetaTypeWrapper dataObjectMetaTypeWrapper : beanMetaTypeWrappers) {
                Class<?> beanType = dataObjectMetaTypeWrapper.getBeanType();
                DataObjectMeta annotation = beanType.getAnnotation(DataObjectMeta.class);

                if (StringUtils.isEmpty(annotation.tableName())) {
                    continue;
                }

                IDataObjectMeta objectMeta = BeanMetaManager.getBeanMeta(beanType);
                String hostSchema = objectMeta.getSchema();

                // schema匹配 ,hostSchema为空表示默认schema
                if (schema.equals(hostSchema) || (StringUtils.isEmpty(hostSchema) && schema.equals(defaltSchema))) {
                    ret.add(dataObjectMetaTypeWrapper);
                }
            }

            return ret;
        }

        public Class<?> getBeanType() {
            return beanType;
        }

        public void setBeanType(Class<?> beanType) {
            this.beanType = beanType;
        }

    }

}
